package cn.bytest.netty.handler;

import cn.bytest.netty.NettyChannel;
import cn.bytets.rpc.api.message.ResponseBuild;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.timeout.IdleStateEvent;
import org.apache.log4j.Logger;

import java.net.InetSocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@io.netty.channel.ChannelHandler.Sharable
public class NettyServerHandler extends ChannelDuplexHandler {

    private Logger logger = Logger.getLogger(NettyServerHandler.class);

    private final ChannelRunnableHandler handler;

    public NettyServerHandler(ChannelRunnableHandler handler){
        this.handler = handler;
    }

    /**
     * the cache for alive worker channel.
     * <ip:port, dubbo channel>
     */
    private final Map<String, NettyChannel> channels = new ConcurrentHashMap<String, NettyChannel>();

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel());
        System.out.println("===>激活");
        if (channel != null) {
            channels.put(NettyChannel.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()), channel);
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        try {
            channels.remove(NettyChannel.toAddressString((InetSocketAddress) ctx.channel().remoteAddress()));
        } finally {
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel());
        handler.received(channel, msg);
    }


    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        // server will close channel when server don't receive any heartbeat from client util timeout.
        if (evt instanceof IdleStateEvent) {
            NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel());
            try {
                logger.info("IdleStateEvent triggered, close channel " + channel);
                channel.close();
            } finally {
                NettyChannel.removeChannelIfDisconnected(ctx.channel());
            }
        }
        super.userEventTriggered(ctx, evt);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel());
        try {
            channel.send(ResponseBuild.buildErrorResponse(channel.getRequest(),cause));
        } finally {
            System.out.println("===>异常,关闭通道..."+cause.getMessage());
            NettyChannel.removeChannelIfDisconnected(ctx.channel());
        }
    }
}
